﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LogicExpressionCalculator;
using FeatureModelCore.FMTypes;
using FeatureModelCore.Optimized;
using FeatureModelCore.FMInterface;
using FeatureModelCore.FMCrossTreeConstraint;

namespace FeatureModelCore.FMAnalyzer
{
    internal class FeatureModelAnalyzer
    {
        #region Properties
        public OptimizedFeatureModel OptimizedTree { get { return _optimizedTree; } }

        #endregion

        #region Fields
        private FeatureModelTree _tree;
        private OptimizedFeatureModel _optimizedTree;
        private bool _productCached;
        private FMProductCollection _allProducts;

        #endregion
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="tree"></param>
        public FeatureModelAnalyzer(FeatureModelTree tree)
        {
            _tree = tree;
            _optimizedTree = new OptimizedFeatureModel(tree);
            _productCached = false;
            _allProducts = null;
        }

        public FeatureModelTree ToOptimizedFeatureModelTree()
        {
            return _optimizedTree.ToFeatureModelTree();
        }
        /// <summary>
        /// This operation takes a feature model as input and return all the products
        /// represented by the model.
        /// </summary>
        /// <returns></returns>
        public FMProductCollection AllProducts()
        {
            if (_productCached == true)
            {
                return _allProducts;
            }
            FMProductCollection allProducts = allProductsWithOptimized();
            this._allProducts = allProducts;
            this._productCached = true;

            // check valid node
            //foreach (FeatureTreeNode node in _tree.AllNodes)
            //{
            //    node.IsDeadNode = true;
            //}
            foreach (FMProduct product in allProducts.AllProducts)
            {
                foreach (FMProductItem item in product.Items)
                {
                    IFeature tmpNode = _tree.GetFeatureNode(item.ItemName);
                    if (tmpNode == null)
                    {
                        continue;
                    }
                    //tmpNode.IsDeadNode = false;
                }
            }

            return allProducts;
        }
        /// <summary>
        /// all product
        /// </summary>
        /// <returns></returns>
        //private FMProductCollection allProductsWithoutOptimized()
        //{
        //    LogicExpression logicExpr = new LogicExpression();
        //    // generate operand, each tree node is a operand
        //    foreach (FeatureTreeNode node in _tree.AllNodes)
        //    {
        //        logicExpr.GenerateOperand(node.FeatureName);
        //    }
        //    logicExpr.GenerateOperand(((FeatureTreeRoot)_tree.Root).FeatureName);

        //    // generate expression objects
        //    AbstractExpressionObject[] objList = ToLogicExpression(_tree, logicExpr);
        //    foreach (AbstractExpressionObject obj in objList)
        //    {
        //        logicExpr.Add(obj);
        //    }

        //    // calculate, to generate id list
        //    List<string[]> result = logicExpr.CalculateLogicSatisfy();

        //    List<FMProductItem> items = new List<FMProductItem>();
            
        //    FMProductCollection productResult = new FMProductCollection();
        //    // parse the result to Products Set
        //    foreach (string[] pro in result)
        //    {
        //        FMProduct tmpPro = new FMProduct();
        //        foreach (string featureName in pro)
        //        {
        //            tmpPro.AddItem(featureName);
        //        }
        //        productResult.Add(tmpPro);
        //    }
        //    return productResult;
        //}
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private FMProductCollection allProductsWithOptimized()
        {
            LogicExpression logicExpr = new LogicExpression();
            // generate operand, each tree node is a operand
            foreach (IOptimizableFeature node in _optimizedTree.AllNodes)
            {
                logicExpr.GenerateOperand(node.ID.ToString());
            }

            // generate expression objects
            AbstractExpressionObject[] objList = ToLogicExpression(_optimizedTree, logicExpr);
            foreach (AbstractExpressionObject obj in objList)
            {
                logicExpr.Add(obj);
            }

            // calculate, to generate id list
            List<string[]> result = logicExpr.CalculateLogicSatisfy();

            List<FMProductItem> itemsList = new List<FMProductItem>();
            foreach (IFeature node in _tree.AllFeatureNodes)
            {
                itemsList.Add(new FMProductItem(node.FeatureName));
            }

            FMProductCollection productResult = new FMProductCollection(itemsList.ToArray<FMProductItem>());
            // parse the result to Products Set
            foreach (string[] pro in result)
            {
                FMProduct tmpPro = new FMProduct();
                foreach (string id in pro)
                {
                    IOptimizableFeature node = (IOptimizableFeature)_optimizedTree[id];
                    tmpPro.AddItem(node.FeatureName);
                    foreach (OptimizedPropertyForFeature prop in node.PropertiesForFeature)
                    {
                        tmpPro.AddItem(((IFeature)prop.StandardObject).FeatureName);
                    }
                }
                productResult.Add(tmpPro);
            }

            return productResult;
        }

        #region To Optimized Logic Expression
        /// <summary>
        /// Turn feature model tree to logic expression
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static AbstractExpressionObject[] ToLogicExpression(OptimizedFeatureModel tree,
            LogicExpression expr)
        {
            List<AbstractExpressionObject[]> andList = new List<AbstractExpressionObject[]>();
            // add root <=> true
            andList.Add(ToLogicExpression((IOptimizableFeature)tree.Root, expr));

            foreach (OptimizedGroupRelationship gr in tree.AllGroupRelationships)
            {
                andList.Add(ToLogicExpression(gr, expr));
            }
            foreach (OptimizedConstraint cons in tree.AllCrossTreeConstraints)
            {
                andList.Add(ToLogicExpression(cons, expr));
            }

            return ExpressionGenerator.Group(LogicOperator.Conjunction
                , andList.ToArray<AbstractExpressionObject[]>());
        }
        /// <summary>
        /// Turn Group Relationship To Logic Expression
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static AbstractExpressionObject[] ToLogicExpression(OptimizedGroupRelationship gR
            , LogicExpression expr)
        {
            switch (gR.Type)
            {
                case FMTreeGroupRelationshipType.GroupOptional:
                    {
                        List<AbstractExpressionObject[]> childList = new List<AbstractExpressionObject[]>();
                        AbstractExpressionObject[] parentEprObjs = ToLogicExpression((IOptimizableFeature)gR.ParentNode, expr);

                        if (parentEprObjs == null)
                        {
                            throw new Exception();
                        }
                        foreach (OptimizedFeature n in gR.ChildrenNodes)
                        {
                            childList.Add(ExpressionGenerator.GeneratorTwoOperandExpression(
                                LogicOperator.Implication, ToLogicExpression(n, expr)
                                , parentEprObjs));
                        }

                        return ExpressionGenerator.Group(LogicOperator.Disjunction
                            , childList.ToArray<AbstractExpressionObject[]>());
                    }
                case FMTreeGroupRelationshipType.GroupMandatory:
                    {
                        List<AbstractExpressionObject[]> childList = new List<AbstractExpressionObject[]>();
                        foreach (OptimizedFeature n in gR.ChildrenNodes)
                        {
                            childList.Add(ToLogicExpression(n, expr));
                        }
                        AbstractExpressionObject parentEprObj = parentEprObj = ToLogicExpression((IOptimizableFeature)gR.ParentNode, expr)[0];
                        if (parentEprObj == null)
                        {
                            throw new Exception();
                        }
                        return groupExpression(LogicOperator.Conjunction
                            , childList.ToArray<AbstractExpressionObject[]>()
                            , parentEprObj);
                    }
                case FMTreeGroupRelationshipType.Or:
                    {
                        List<AbstractExpressionObject[]> childList = new List<AbstractExpressionObject[]>();
                        foreach (OptimizedFeature n in gR.ChildrenNodes)
                        {
                            childList.Add(ToLogicExpression(n, expr));
                        }

                        AbstractExpressionObject parentEprObj = null;
                        #region Get Parent expression Objects
                        parentEprObj = ToLogicExpression(gR.ParentNode, expr)[0];
                        if (parentEprObj == null)
                        {
                            throw new Exception();
                        }
                        #endregion

                        return groupExpression(LogicOperator.Disjunction
                            , childList.ToArray<AbstractExpressionObject[]>()
                            , parentEprObj);
                    }
                case FMTreeGroupRelationshipType.Alternative:
                    {
                        // and list
                        List<AbstractExpressionObject[]> andList = new List<AbstractExpressionObject[]>();
                        foreach (OptimizedFeature n in gR.ChildrenNodes)
                        {
                            List<AbstractExpressionObject[]> andGroupList = new List<AbstractExpressionObject[]>();

                            AbstractExpressionObject[] parentEprObjs = null;
                            #region Get Parent expression Objects
                            parentEprObjs = ToLogicExpression(gR.ParentNode, expr);
                            if (parentEprObjs == null)
                            {
                                throw new Exception();
                            }
                            #endregion

                            andGroupList.Add(parentEprObjs);
                            // put C nagative in list
                            foreach (OptimizedFeature subN in gR.ChildrenNodes)
                            {
                                if (subN != n)
                                {
                                    andGroupList.Add(
                                    ExpressionGenerator.GeneratorOneOperandExpression(
                                        ToLogicExpression(subN, expr)));
                                }
                            }
                            // negative list
                            AbstractExpressionObject[] tmpList = groupExpression
                                (LogicOperator.Conjunction, andGroupList.ToArray<AbstractExpressionObject[]>()
                                , ToLogicExpression(n, expr)[0]);
                            andList.Add(tmpList);
                        }
                        return ExpressionGenerator.Group(LogicOperator.Conjunction
                            , andList.ToArray<AbstractExpressionObject[]>());
                    }
                default:
                    {
                        throw new Exception();
                    }
            }
        }
        /// <summary>
        /// Turn Relationship To Logic Expression
        /// </summary>
        /// <param name="relationship"></param>
        /// <param name="expr"></param>
        /// <returns></returns>
        //public static AbstractExpressionObject[] ToLogicExpression(OptimizedFeatureTreeRelationship r,
        //    LogicExpression expr)
        //{
        //    AbstractExpressionObject[] parentEprObjs = null;
        //    #region Get Parent expression Objects
        //    parentEprObjs = ToLogicExpression(r.ParentNode, expr);
        //    if (parentEprObjs == null)
        //    {
        //        throw new Exception();
        //    }
        //    #endregion

        //    switch (r.Type)
        //    {
        //        case FMTreeRelationshipType.MANDATORY:
        //            {
        //                return ExpressionGenerator.GeneratorTwoOperandExpression(
        //                    LogicOperator.Biconditional, parentEprObjs
        //                    , ToLogicExpression(r.ChildNode, expr));
        //            }
        //        case FMTreeRelationshipType.OPTIONAL:
        //            {
        //                return ExpressionGenerator.GeneratorTwoOperandExpression(
        //                    LogicOperator.Implication, ToLogicExpression(r.ChildNode, expr)
        //                    , parentEprObjs);
        //            }
        //        default:
        //            {
        //                throw new Exception("No such Relationship Type");
        //            }
        //    }

        //}
        /// <summary>
        /// Turn Tree Node to logic expression
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static AbstractExpressionObject[] ToLogicExpression(IOptimizableFeature node
            , LogicExpression expr)
        {
            return new AbstractExpressionObject[] { expr[node.ID.ToString()] };
        }
        /// <summary>
        /// Turn Constraint to Logic Expression
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static AbstractExpressionObject[] ToLogicExpression(OptimizedConstraint cons
            , LogicExpression expr)
        {
            LogicOperand operandA = expr[cons.NodeIDA.ToString()];
            LogicOperand operandB = expr[cons.NodeIDB.ToString()];
            if (cons.Type == FMCrossTreeConstraintType.Exclude)
            {
                return new AbstractExpressionObject[]
                {
                    LogicOperator.Negation,
                    LogicOperator.Conjunction,
                    operandA,
                    operandB
                };
            }
            else if (cons.Type == FMCrossTreeConstraintType.Require)
            {
                return new AbstractExpressionObject[]
                {
                    LogicOperator.Implication,
                    operandA,
                    operandB
                };
            }
            throw new NotImplementedException();
        }
        #endregion
        #region To Logic Expression
        /// <summary>
        /// 
        /// </summary>
        /// <param name="oper"></param>
        /// <returns></returns>
        private static AbstractExpressionObject[] groupExpression(LogicOperator oper
            , AbstractExpressionObject[][] children, AbstractExpressionObject parent)
        {
            AbstractExpressionObject[] childRet = ExpressionGenerator.Group(oper, children);
            List<AbstractExpressionObject> ret = new List<AbstractExpressionObject>();
            ret.Add(LogicOperator.Biconditional);
            ret.Add(parent);
            ret.AddRange(childRet);
            return ret.ToArray<AbstractExpressionObject>();
        }
        /// <summary>
        /// Turn feature model tree to logic expression
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static AbstractExpressionObject[] ToLogicExpression(FeatureModelTree tree,
            LogicExpression expr)
        {
            List<AbstractExpressionObject[]> andList = new List<AbstractExpressionObject[]>();
            foreach (FeatureTreeGroupRelationship gr in tree.AllGroupRelationships)
            {
                andList.Add(ToLogicExpression(gr, expr));
            }
            foreach (CrossTreeConstraint cons in tree.AllCrossTreeConstraints)
            {
                andList.Add(ToLogicExpression(cons, expr));
            }

            return ExpressionGenerator.Group(LogicOperator.Conjunction
                , andList.ToArray<AbstractExpressionObject[]>());
        }
        /// <summary>
        /// Turn Group Relationship To Logic Expression
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static AbstractExpressionObject[] ToLogicExpression(FeatureTreeGroupRelationship gR
            , LogicExpression expr)
        {

            switch (gR.Type)
            {
                case FMTreeGroupRelationshipType.GroupOptional:
                    {
                        List<AbstractExpressionObject[]> childList = new List<AbstractExpressionObject[]>();
                        AbstractExpressionObject[] parentEprObjs = null;
                        parentEprObjs = ToLogicExpression(gR.ParentNode, expr);
                        if (parentEprObjs == null)
                        {
                            throw new Exception();
                        }
                        foreach (FeatureTreeNode n in gR.ChildrenNodes)
                        {
                            childList.Add(ExpressionGenerator.GeneratorTwoOperandExpression(
                                LogicOperator.Implication, ToLogicExpression(n, expr)
                                , parentEprObjs));
                        }

                        return ExpressionGenerator.Group(LogicOperator.Disjunction
                            , childList.ToArray<AbstractExpressionObject[]>());
                    }
                case FMTreeGroupRelationshipType.GroupMandatory:
                    {
                        List<AbstractExpressionObject[]> childList = new List<AbstractExpressionObject[]>();
                        foreach (FeatureTreeNode n in gR.ChildrenNodes)
                        {
                            childList.Add(ToLogicExpression(n, expr));
                        }
                        AbstractExpressionObject parentEprObj = null;
                        #region Get Parent expression Objects
                        parentEprObj = ToLogicExpression(gR.ParentNode, expr)[0];
                        if (parentEprObj == null)
                        {
                            throw new Exception();
                        }
                        #endregion
                        return groupExpression(LogicOperator.Conjunction
                            , childList.ToArray<AbstractExpressionObject[]>()
                            , parentEprObj);
                    }
                case FMTreeGroupRelationshipType.Or:
                    {
                        List<AbstractExpressionObject[]> childList = new List<AbstractExpressionObject[]>();
                        foreach (FeatureTreeNode n in gR.ChildrenNodes)
                        {
                            childList.Add(ToLogicExpression(n, expr));
                        }

                        AbstractExpressionObject parentEprObj = null;
                        #region Get Parent expression Objects
                        parentEprObj = ToLogicExpression(gR.ParentNode, expr)[0];
                        if (parentEprObj == null)
                        {
                            throw new Exception();
                        }
                        #endregion

                        return groupExpression(LogicOperator.Disjunction
                            , childList.ToArray<AbstractExpressionObject[]>()
                            , parentEprObj);
                    }
                case FMTreeGroupRelationshipType.Alternative:
                    {
                        // and list
                        List<AbstractExpressionObject[]> andList = new List<AbstractExpressionObject[]>();
                        foreach (FeatureTreeNode n in gR.ChildrenNodes)
                        {
                            List<AbstractExpressionObject[]> andGroupList = new List<AbstractExpressionObject[]>();

                            AbstractExpressionObject[] parentEprObjs = null;
                            #region Get Parent expression Objects
                            parentEprObjs = ToLogicExpression(gR.ParentNode, expr);
                            if (parentEprObjs == null)
                            {
                                throw new Exception();
                            }
                            #endregion

                            andGroupList.Add(parentEprObjs);
                            // put C nagative in list
                            foreach (FeatureTreeNode subN in gR.ChildrenNodes)
                            {
                                if (subN != n)
                                {
                                    andGroupList.Add(
                                    ExpressionGenerator.GeneratorOneOperandExpression(
                                        ToLogicExpression(subN, expr)));
                                }
                            }
                            // negative list
                            AbstractExpressionObject[] tmpList = groupExpression
                                (LogicOperator.Conjunction, andGroupList.ToArray<AbstractExpressionObject[]>()
                                , ToLogicExpression(n, expr)[0]);
                            andList.Add(tmpList);
                        }
                        return ExpressionGenerator.Group(LogicOperator.Conjunction
                            , andList.ToArray<AbstractExpressionObject[]>());
                    }
                default:
                    {
                        throw new Exception();
                    }
            }
        }
        /// <summary>
        /// Turn Relationship To Logic Expression
        /// </summary>
        /// <param name="relationship"></param>
        /// <param name="expr"></param>
        /// <returns></returns>
        //public static AbstractExpressionObject[] ToLogicExpression(FeatureTreeRelationship r,
        //    LogicExpression expr)
        //{
        //    AbstractExpressionObject[] parentEprObjs = null;
        //    #region Get Parent expression Objects
        //    try
        //    {
        //        parentEprObjs = ToLogicExpression(r.ParentNode, expr);
        //    }
        //    catch (Exception)
        //    {
        //    }
        //    try
        //    {
        //        parentEprObjs = ToLogicExpression(r.ParentRoot, expr);
        //    }
        //    catch (Exception)
        //    {
        //    }
        //    if (parentEprObjs == null)
        //    {
        //        throw new Exception();
        //    }
        //    #endregion

        //    switch (r.Type)
        //    {
        //        case FMTreeRelationshipType.MANDATORY:
        //            {
        //                return ExpressionGenerator.GeneratorTwoOperandExpression(
        //                    LogicOperator.Biconditional, parentEprObjs
        //                    , ToLogicExpression(r.ChildNode, expr));
        //            }
        //        case FMTreeRelationshipType.OPTIONAL:
        //            {
        //                return ExpressionGenerator.GeneratorTwoOperandExpression(
        //                    LogicOperator.Implication, ToLogicExpression(r.ChildNode, expr)
        //                    , parentEprObjs);
        //            }
        //        default:
        //            {
        //                throw new Exception("No such Relationship Type");
        //            }
        //    }

        //}
        /// <summary>
        /// Turn Tree Node to logic expression
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static AbstractExpressionObject[] ToLogicExpression(IFeature node
            , LogicExpression expr)
        {
            return new AbstractExpressionObject[] { expr[node.ID.ToString()] };
        }
        /// <summary>
        /// Turn Constraint to Logic Expression
        /// </summary>
        /// <param name="expr"></param>
        /// <returns></returns>
        public static AbstractExpressionObject[] ToLogicExpression(CrossTreeConstraint cons
            , LogicExpression expr)
        {
            LogicOperand operandA = expr[cons.NodeA.FeatureName];
            LogicOperand operandB = expr[cons.NodeB.FeatureName];
            if (cons.Type == FMCrossTreeConstraintType.Exclude)
            {
                return new AbstractExpressionObject[]
                {
                    LogicOperator.Negation,
                    LogicOperator.Conjunction,
                    operandA,
                    operandB
                };
            }
            else if (cons.Type == FMCrossTreeConstraintType.Require)
            {
                return new AbstractExpressionObject[]
                {
                    LogicOperator.Implication,
                    operandA,
                    operandB
                };
            }
            throw new NotImplementedException();
        }
        #endregion
    }
}
